/*
 * tcp_server.h
 *
 *  Created on: 2016-2-28
 *      Author: ZhangXiyin
 */

#ifndef EYAS_SOURCES_IO_SOCKET_TCP_TCP_SERVER_H_
#define EYAS_SOURCES_IO_SOCKET_TCP_TCP_SERVER_H_

#include <boost/enable_shared_from_this.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include "../../conf/def.h"

namespace eyas {
namespace io {

class tcp_server : public boost::enable_shared_from_this<tcp_server> {
private:
	class connect_callback_function_base{
	public:
		connect_callback_function_base() = default;
		virtual ~connect_callback_function_base() = default;
	public:
		virtual void call(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error) = default;
	};
	typedef boost::shared_ptr<connect_callback_function_base> connect_callback_function_ptr;
	template <typename F>
	class tcp_server_connect_callback : public connect_callback_function_base{
	public:
		tcp_server_connect_callback(EYAS_IO_RV_REF(F) f):f_(boost::forward(f)){};
		virtual ~tcp_server_connect_callback() = default;
	private:
		F f_;
	public:
		virtual void call(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error){
			f_(conn_ptr, error);
		}
	};

	template <typename T>
	static tcp_server::connect_callback_function_ptr create_new(EYAS_IO_RV_REF(T) f){
		return gc::create_new<tcp_server::tcp_server_connect_callback<boost::remove_reference<T>>>(boost::forward(f));
	}
public:
	tcp_server();
	virtual ~tcp_server();
private:
	boost::asio::io_service& io_service_;
	boost::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor_ptr_;
	boost::asio::ip::tcp::endpoint localpoint_;
	bool disable_;

	boost::recursive_mutex mutex_;
	connect_callback_function_ptr conn_callback_ptr_;

	void handle_accept(connection_ptr& new_connection, const boost::system::error_code& error);
	void ready_accept();
public:
	bool open(const char* ip, unsigned short port,
			void (*connected_callback)(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error));
	template <class T, typename T_Obj>
	bool open(const char* ip, unsigned short port,
			void (T::*connected_callback)(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error), T_Obj obj);

	template <typename F, typename... Args>
	bool open(const char* ip, unsigned short port,
			F f,
			Args... args);

	void close();
};

inline bool tcp_server::open(const char* ip, unsigned short port,
		void (*connected_callback)(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error)){
	boost::recursive_mutex::scoped_lock lck(this->mutex_);
	this->localpoint_.address(boost::asio::ip::address::from_string(ip));
	this->localpoint_.port(port);
	this->conn_callback_ptr_ = tcp_server::create_new(
			boost::bind(
					connected_callback, boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error
					));
	try{
		this->disable_ = false;
		this->acceptor_ptr_.reset(new boost::asio::ip::tcp::acceptor(service::instance()->io_service(), this->localpoint_));
		this->acceptor_ptr_->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
		this->ready_accept();
		return true;
	}catch(boost::system::system_error& err){
		std::cerr << err.what() << std::endl;
		return false;
	}
}
template <class T, typename T_Obj>
inline bool tcp_server::open(const char* ip, unsigned short port,
		void (T::*connected_callback)(tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error), T_Obj obj){
	boost::recursive_mutex::scoped_lock lck(this->mutex_);
	this->localpoint_.address(boost::asio::ip::address::from_string(ip));
	this->localpoint_.port(port);
	this->conn_callback_ptr_ = tcp_server::create_new(
				boost::bind(
						connected_callback, obj, boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error
						));
	try{
		this->disable_ = false;
		this->acceptor_ptr_.reset(new boost::asio::ip::tcp::acceptor(service::instance()->io_service(), this->localpoint_));
		this->acceptor_ptr_->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
		this->ready_accept();
		return true;
	}catch(boost::system::system_error& err){
		std::cerr << err.what() << std::endl;
		return false;
	}
}

template <typename F, typename... Args>
inline bool tcp_server::open(const char* ip, unsigned short port,
		F f,
		Args... args){
	boost::recursive_mutex::scoped_lock lck(this->mutex_);
	this->localpoint_.address(boost::asio::ip::address::from_string(ip));
	this->localpoint_.port(port);
	this->conn_callback_ptr_ = tcp_server::create_new(
					boost::bind(
							f, args..., boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error
							));
	try{
		this->disable_ = false;
		this->acceptor_ptr_.reset(new boost::asio::ip::tcp::acceptor(service::instance()->io_service(), this->localpoint_));
		this->acceptor_ptr_->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
		this->ready_accept();
		return true;
	}catch(boost::system::system_error& err){
		std::cerr << err.what() << std::endl;
		return false;
	}
}


} /* namespace io */
} /* namespace eyas */
#endif /* EYAS_SOURCES_IO_SOCKET_TCP_TCP_SERVER_H_ */
